package org.budo.launcher.dubbo;

import java.net.URL;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

import org.budo.launcher.service.AbstractBudoLauncherService;

import com.alibaba.dubbo.common.Constants;
import com.alibaba.dubbo.common.extension.ExtensionLoader;
import com.alibaba.dubbo.common.logger.Logger;
import com.alibaba.dubbo.common.logger.LoggerFactory;
import com.alibaba.dubbo.common.utils.ConfigUtils;
import com.alibaba.dubbo.container.Container;
import com.alibaba.dubbo.container.Main;
import com.alibaba.dubbo.container.spring.SpringContainer;

/**
 * Dubbo 启动器
 * 
 * @author lmw
 */
public class BudoDubboLauncher extends AbstractBudoLauncherService {
    private static final Logger log = LoggerFactory.getLogger(BudoDubboLauncher.class);

    private static final ExtensionLoader<Container> containerLoader = ExtensionLoader.getExtensionLoader(Container.class);

    private static final ReentrantLock LOCK = new ReentrantLock();

    private static final Condition STOP = LOCK.newCondition();

    public static final String DEFAULT_SPRING_CONFIG = "classpath*:META-INF/spring/spring.xml";

    @Override
    public void run(String[] args) {
        System.setProperty(SpringContainer.SPRING_CONFIG, this.getSpringXmlPath());

        _call_dubbo_main(args);
    }

    /**
     * @see com.alibaba.dubbo.container.Main#main(String[])
     */
    private void _call_dubbo_main(String[] args) {
        try {
            List<Container> containers = this.getContainers(args);

            Thread shutdownHookThread = this.shutdownHookThread(containers);
            Runtime.getRuntime().addShutdownHook(shutdownHookThread);

            for (Container container : containers) {
                container.start();
                log.info("#63 Dubbo " + container.getClass().getSimpleName() + " started!");
            }

            System.out.println("#66 " + new SimpleDateFormat("[yyyy-MM-dd HH:mm:ss]").format(new Date()) + " Dubbo service server started!");
        } catch (RuntimeException e) {
            e.printStackTrace();
            log.error("#69 " + e.getMessage(), e);
            System.exit(1);
        }

        try {
            LOCK.lock();
            STOP.await();
        } catch (InterruptedException e) {
            log.warn("#77 Dubbo service server stopped, interrupted by other thread!", e);
        } finally {
            LOCK.unlock();
        }
    }

    private List<Container> getContainers(String[] args) {
        // main 参数指定的 container
        List<Container> containers = this.resolveContainersByKeys(args);

        // 如果无效，使用默认container
        String containerConfig = null;
        if (null == containers || containers.isEmpty()) {
            containerConfig = ConfigUtils.getProperty(Main.CONTAINER_KEY, containerLoader.getDefaultExtensionName());
            String[] containerKeys = Constants.COMMA_SPLIT_PATTERN.split(containerConfig);
            containers = this.resolveContainersByKeys(containerKeys);

            log.warn("#61 containerConfig=" + containerConfig + ", containers=" + containers);
        }

        log.info("#57 Use container type(" + Arrays.toString(args) + ") to run dubbo serivce, containers=" + containers + ", containerConfig=" + containerConfig);
        return containers;
    }

    private List<Container> resolveContainersByKeys(String[] containerKeys) {
        if (null == containerKeys) {
            return null;
        }

        List<Container> containers = new ArrayList<Container>();
        for (int i = 0; i < containerKeys.length; i++) {
            String containerName = containerKeys[i];
            Container container = this.loadContainerExtension(containerName);
            if (null == container) {
                continue;
            }

            containers.add(container);
        }
        return containers;
    }

    private Thread shutdownHookThread(final List<Container> containers) {
        return new Thread() {
            public void run() {
                for (Container container : containers) {
                    try {
                        container.stop();
                        log.info("#102 Dubbo " + container.getClass().getSimpleName() + " stopped!");
                    } catch (Throwable t) {
                        log.error("#104 " + t.getMessage(), t);
                    }
                    try {
                        LOCK.lock();
                        STOP.signal();
                    } finally {
                        LOCK.unlock();
                    }
                }
            }
        };
    }

    private Container loadContainerExtension(String containerName) {
        if (null == containerName || containerName.startsWith("-")) {
            log.warn("#131 no Container for name " + containerName + ", return null");
            return null;
        }

        return containerLoader.getExtension(containerName); 
    }

    private String getSpringXmlPath() {
        URL url = ClassLoader.getSystemResource(DEFAULT_SPRING_CONFIG);

        if (null == url) {
            log.error("#152 getSpringXmlPath return " + DEFAULT_SPRING_CONFIG + ", url=" + url);
        }

        return DEFAULT_SPRING_CONFIG;
    }
}